classdef RandomKLabelsetsOverlapping < RandomKLabelsets
    %RANDOMKLABELSETSOVERLAPPING Random K Labelsets Overlapping (RAkEL_o)
    %   Detailed explanation goes here
    
    properties ( SetAccess = protected )
        rLabelset = 1.5
    end
    
    methods % Set methods
        function [  ] = setRLabelset( this, rLabelset )
            %SETRMODEL Summary of this function goes here
            %   Detailed explanation goes here
            
            assert(rLabelset > 0);
            this.rLabelset = rLabelset;
        end
    end
    
    methods
        function [ this ] = RandomKLabelsetsOverlapping( name, rLabelset, nSubLabel, algorithm0 )
            if nargin == 0
                this.setName('rakel_o');
            end
            if nargin >= 1
                this.setName(name);
            end
            if nargin >= 2
                this.setRModel(rModel);
            end
            if nargin >= 3
                this.setRLabelset(rLabelset);
            end
            this.setAlgorithm0(LabelPowerset());
            if nargin >= 4
                this.setAlgorithm0(algorithm0);
            end
        end
    end
    
    methods
        function [  ] = build( this, X1, Y1 )
            %BUILD Summary of this function goes here
            %   Detailed explanation goes here
            % RandomKLabelsetsOverlapping::build
            
            nLab = size(Y1, 2);
            this.zSubLabelset = generateZLabelset(this.randomSeed, nLab, this.rLabelset, this.nSubLabel);
            
            % nLabelset = length(this.zSubLabelset);
            this.zAlgorithm = cell(1, this.nSubLabelset);
            
            for iLabelset = 1:this.nSubLabelset
                zSubLabelId_i = this.zSubLabelset{iLabelset};
                
                this.zAlgorithm{iLabelset} = this.algorithm0.clone();
                this.zAlgorithm{iLabelset}.build(X1, Y1(:, zSubLabelId_i));
            end
        end
        
        function [ result2 ] = apply( this, X2, Y2 )
            %APPLY Summary of this function goes here
            %   Detailed explanation goes here
            % RandomKLabelsetsOverlapping::apply
            
            nLabelset = length(this.zSubLabelset);
            
            Y2_hat = zeros(size(Y2));
            Y2_out = Y2_hat;
            zLabelWeight = zeros(1, size(Y2, 2));
            
            for iLabelset = 1:nLabelset
                zSubLabelId_i = this.zSubLabelset{iLabelset};
                
                result2_i ...
                    = this.zAlgorithm{iLabelset}.apply(X2, Y2(:, zSubLabelId_i));
                
                zLabelWeight(zSubLabelId_i) = zLabelWeight(zSubLabelId_i) + 1;
                Y2_hat(:, zSubLabelId_i) = Y2_hat(:, zSubLabelId_i) + result2_i.Y_hat;
                Y2_out(:, zSubLabelId_i) = Y2_out(:, zSubLabelId_i) + result2_i.Y_out;
            end
            
            result2.Y_hat = sign(Y2_hat + eps);
            result2.Y_out = Y2_out./repmat(zLabelWeight, size(Y2_out, 1), 1);
        end
    end
    
end

function [ zLabelset ] = generateZLabelset( randomSeed, ...
    nLab, rLabelset, nSubLabel )

rng(randomSeed);

zLabel = 1:nLab;
nLabelset = ceil(nLab*rLabelset);
zTotLabelset = combinator(int8(nLab), nSubLabel, 'c')';
zTotLabelset = double(zTotLabelset);
zTotLabelset = num2cell(zTotLabelset, 1);
nTotLabelset = length(zTotLabelset);
nLabelset = min(nLabelset, nTotLabelset);

zLabelset =  cell(1, nLabelset); % Selected labelsets

zLabel_i = zLabel;
for iLabelset = 1:nLabelset
    while true
        jTotLabelset = randi(length(zTotLabelset));
        if any(ismember(zTotLabelset{jTotLabelset}, zLabel_i))
            zLabelset{iLabelset} = zTotLabelset{jTotLabelset};
            zTotLabelset(jTotLabelset) = []; % Remove this cell
            
            [~, zLabelId_i] = intersect(zLabel_i, zLabelset{iLabelset});
            zLabel_i(zLabelId_i) = [];
            if isempty(zLabel_i)
                zLabel_i = zLabel;
            end
            break
        end
    end
    
end

end
